這幾天我們把 Coroutine 神秘的面紗好像掀開了一點,知道他是用來解決非同步程式的問題,也我們知道四個 Coroutine 的特點。Coroutine 的輪廓漸漸清晰,本篇文章將繼續帶你認識 Coroutine。
Coroutines 是由 cooperation 加上 routine 所組合而成的複合字, cooperation 指共同合作,這裡的 routine 意指 function、method,意思是協同處理多個程序(協程)。
Routine(n.) - a sequence of computer instructions for performing a particular task - Merriam-webster
在維基百科是這樣說的
Coroutines are computer program components that generalize subroutines for non-preemptive multitasking, by allowing execution to be suspended and resumed.
Coroutines 它是在語言層面上實作的,是屬於非搶佔式多工 aka 協同式多工。允許計算能夠被暫停以及恢復。
Coroutine 被拿來與執行緒做比較,那麼它們兩個到底有什麼不同呢?
首先,我們知道每一個應用程式 (Application) 有一個程序 (Process) 以及一個執行緒 (Thread)。程序有一塊獨立的記憶體用來把自己應用程式所需的資源與其他應用程式的資源隔離開來,並且依照需要建立執行緒來執行任務。
雖然每一個程序可以建立一個或多個執行緒,但是它們與其他程序所共同分享的是 CPU 的使用時間,系統會根據執行緒的優先權來分配 CPU 時間給執行緒來使用,也就是說,只要有一個優先權較高的任務準備好,就可以中斷當下優先權較低的任務。這種由系統根據執行緒的優先權來分配 CPU 時間的多工方式稱作搶佔式多工(Preemptive multitasking)。
Coroutine 是一種協作式多工 (Cooperative multitasking),相較於搶佔式多工 ,協作式多工要求每一個運行中的程式,定時放棄自己的執行權利,告知作業系統可讓下一個程式執行。也就是說協程的調度是由協程自行控制,而不是由系統來決定,畢竟在系統的角度下根本不知道有 Coroutine 的存在。
每一個程序有各自的記憶體,在程序底下的每一個執行緒共用這塊記憶體,但是每一個執行緒都有各自的呼叫棧(call stack),這個堆疊紀錄所有執行的函式以及變數,所以在執行緒中可以透過 Reference 取得相同的物件,在使用多執行緒的時候,必須要考慮資料的共用,不正確的使用,可能會造成錯誤發生。
在多執行緒的情況下,在不同的執行緒切換時,需要將目前程序的內容記錄下來,在之後切換回來的時候就可以依照這個紀錄的內容回到原本的位置。
Kotlin 的 coroutine 則是在每一個 suspend 函式裡面都隱藏著一個 Continuation 實例 ,Kotlin 的 coroutine 暫停時(suspend),就會把當下的資訊儲存在 Continuation 物件中,Continuation 把這些資訊帶著走,當 suspend 函式完成之後,便會把利用 Continuation 儲存的資訊切回原本的 Coroutine,所以在 coroutine 中,因為只需要一個 Continuation 物件儲存上下文資訊,所以消耗的資源相對的比較少。這類的 coroutine 稱之為無棧協程(Stackless coroutine)。那什麼是 Continuation 呢?其實就是一個 Callback。
所以 suspend 必須在 coroutine scope 中執行,因為在 coroutine scope 也包含了Continuation。
Kotlin 的 coroutine 是採用協同式多工,協同式多工是讓 coroutine 自行決定任務的調度,而在 Kotlin 的 coroutine 是採用 Stackless coroutine 的設計,這麼做的好處就是可以比執行緒更輕便,不過就無法再任意的地方呼叫 suspend 函式,必須要包含在 coroutine scope 中才能,因為 Coroutine scope 隱含著一個 Continuation 來處理 coroutine 的切換。
Kotlin Taiwan User Group
Kotlin 讀書會
有興趣的讀者歡迎參考:https://coroutine.kotlin.tips/
天瓏書局